﻿using System;
using System.IO;
using System.Runtime.InteropServices;

namespace Sunny.UI
{
    public interface IStructFile
    {
        public void Init();

        public string FileName();
    }

    public static class UStruct
    {
        /// <summary>
        /// 结构序列化字符串
        /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        /// </summary>
        public const string StructLayoutString = "[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]";

        /// <summary>
        /// 结构序列化字符串数组长度
        /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        /// </summary>
        public const string StructMarshalAs = "[MarshalAs(UnmanagedType.ByValArray, SizeConst = 124)]";

        /// <summary>
        /// 字节数组转为值类型的对象
        /// </summary>
        /// <typeparam name="T">值类型</typeparam>
        /// <param name="bytes">字节数组</param>
        /// <returns>对象</returns>
        public static T ToStruct<T>(this byte[] bytes) where T : struct
        {
            if (bytes == null)
            {
                throw new NullReferenceException();
            }

            //得到结构体的大小
            int size;
            try
            {
                size = Marshal.SizeOf(typeof(T));
            }
            catch (Exception)
            {
                throw new NullReferenceException();
            }

            if (size > bytes.Length)
            {
                throw new NullReferenceException();
            }

            //分配结构体大小的内存空间
            IntPtr structPtr = Marshal.AllocHGlobal(size);
            //将byte数组拷到分配好的内存空间
            Marshal.Copy(bytes, 0, structPtr, size);
            //将内存空间转换为目标结构体
            var obj = Marshal.PtrToStructure(structPtr, typeof(T));
            //释放内存空间
            Marshal.FreeHGlobal(structPtr);
            //返回结构体
            return (T)obj;
        }

        /// <summary>
        /// 值类型对象长度
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="obj">obj</param>
        /// <returns>长度</returns>
        public static int Size<T>(this T obj) where T : struct
        {
            return Marshal.SizeOf(typeof(T));
        }

        /// <summary>
        /// 值类型的对象转换为字节数组
        /// </summary>
        /// <typeparam name="T">值类型</typeparam>
        /// <param name="obj">对象</param>
        /// <returns>字节数组</returns>
        public static byte[] ToBytes<T>(this T obj) where T : struct
        {
            //得到结构体的大小
            int size = Marshal.SizeOf(typeof(T));
            //创建byte数组
            var bytes = new byte[size];
            //分配结构体大小的内存空间
            IntPtr structPtr = Marshal.AllocHGlobal(size);
            //将结构体拷到分配好的内存空间
            Marshal.StructureToPtr(obj, structPtr, false);
            //从内存空间拷到byte数组
            Marshal.Copy(structPtr, bytes, 0, size);
            //释放内存空间
            Marshal.FreeHGlobal(structPtr);
            //返回byte数组
            return bytes;
        }

        public static T Load<T>(this T obj, string path, string fileName = "") where T : struct, IStructFile
        {
            string file = fileName.IsValid() ? fileName : obj.FileName();
            if (File.Exists(path.DealPath() + file))
            {
                byte[] bts = File.ReadAllBytes(path.DealPath() + file);
                obj = bts.ToStruct<T>();
            }
            else
            {
                obj = new T();
                obj.Init();
            }

            return obj;
        }

        public static void Save<T>(this T obj, string path, string fileName = "") where T : struct, IStructFile
        {
            string file = fileName.IsValid() ? fileName : obj.FileName();
            byte[] bts = obj.ToBytes();
            File.WriteAllBytes(path.DealPath() + file, bts);
        }
    }
}
